#pragma once

#include <memory>

template<class T>
struct CRefCountedTraits
{
	static void CheckPointer(T * ptr) // throw(exception)
	{}
};

template<class T>
class CRefCounted
{
public:
	typedef CRefCounted<T>	PtrT;
	typedef T				ValT;
	
	CRefCounted()
		: m_pImpl(NULL)
	{
	}
	explicit CRefCounted(T * ptr)
		: m_pImpl(ptr)
	{
		if(m_pImpl.get())
			InterlockedIncrement(&m_pImpl->RefCounter());
	}
	CRefCounted(const CRefCounted<T> & src)
	{
		if(src.m_pImpl.get())
			InterlockedIncrement(&src.m_pImpl->RefCounter());
		m_pImpl = std::auto_ptr<T>(src.m_pImpl.get());
	}
	void Swap(CRefCounted<T> & src)
	{
		std::swap(m_pImpl, src.m_pImpl);
	}
	CRefCounted<T> & operator = (const CRefCounted<T> & src)
	{
		CRefCounted<T> temp(src);
		Swap(temp);
		return *this;
	}
	~CRefCounted()
	{
		if(m_pImpl.get())
			if(InterlockedDecrement(&m_pImpl->RefCounter()))
				m_pImpl.release();
	}
	T * operator -> ()
	{
		CRefCountedTraits<T>::CheckPointer(m_pImpl.get());
		return m_pImpl.get();
	}
	const T * operator -> () const
	{
		CRefCountedTraits<T>::CheckPointer(m_pImpl.get());
		return m_pImpl.get();
	}
	T & operator * ()
	{
		CRefCountedTraits<T>::CheckPointer(m_pImpl.get());
		return *m_pImpl.get();
	}
	const T & operator * () const
	{
		CRefCountedTraits<T>::CheckPointer(m_pImpl.get());
		return *m_pImpl.get();
	}
	bool operator == (const CRefCounted<T> & src) const 
	{
		return m_pImpl.get() == src.m_pImpl.get();
	}
	bool operator != (const CRefCounted<T> & src) const 
	{
		return !(operator == (src));
	}
	bool operator < (const CRefCounted<T> & src) const 
	{
		return m_pImpl.get() < src.m_pImpl.get();
	}
	
	T * impl()
	{
		CRefCountedTraits<T>::CheckPointer(m_pImpl.get());
		return m_pImpl.get();
	}
	const T * impl() const
	{
		CRefCountedTraits<T>::CheckPointer(m_pImpl.get());
		return m_pImpl.get();
	}
	void attach(T * p)
	{
		m_pImpl = std::auto_ptr<T>(p);
	}
	T * detach()
	{
		std::auto_ptr<T> pImpl;
		swap(m_pImpl, pImpl);
		return pImpl.release();
	}

	bool isNull() const
	{
		return m_pImpl.get() == NULL;
	}

	void reset(T * p = NULL)
	{
		if(m_pImpl.get())
		{
			if(InterlockedDecrement(&m_pImpl->RefCounter()))
				m_pImpl.release();
		}

		m_pImpl = std::auto_ptr<T>(p);
		if(m_pImpl.get())
			InterlockedIncrement(&m_pImpl->RefCounter());
	}
	
private:
	std::auto_ptr<T> m_pImpl;
};

template<class T>
class CSafeRefCounted : public CRefCounted<T>
{
public:
	CSafeRefCounted()
		: CRefCounted<T>()
	{}
	explicit CSafeRefCounted(T * ptr)
		: CRefCounted<T>(ptr)
	{}
	CSafeRefCounted(const CSafeRefCounted<T> & src)
		: CRefCounted<T>(src)
	{}
	CSafeRefCounted<T> & operator = (const CSafeRefCounted<T> & src)
	{
		CSafeRefCounted<T> temp(src);
		Swap(temp);
		return *this;
	}
	~CSafeRefCounted()
	{
		if(!isNull() && InterlockedExchangeAdd(&impl()->RefCounter(), 0) == 1)
		{
			impl()->destroy();
		}
	}
};

// -------------------------------------------------------------------------- //
/*
 *	CCrossModuleRefCounted class

 *	the reference counting wrapper for cross-module objects.
 */
// -------------------------------------------------------------------------- //

template<class T>
class CCrossModuleRefCounted : public CRefCounted<T>
{
public:
	
	CCrossModuleRefCounted ()
	: CRefCounted<T>()
	{
	}
	
	explicit CCrossModuleRefCounted (T* pSrc)
	: CRefCounted<T>(pSrc)
	{
	}
	
	CCrossModuleRefCounted (const CCrossModuleRefCounted<T>& src)
	: CRefCounted<T>(src)
	{
	}
	
	~CCrossModuleRefCounted ()
	{
		if (!isNull() && InterlockedExchangeAdd(&impl()->RefCounter(), 0) == 1)
		{
			detach()->deallocate();
		}
	}
};

// -------------------------------------------------------------------------- //

template<class T>
class CRefCountedAdapter : public T
{
public:
	CRefCountedAdapter()
		: T()
		, m_dwRefCounter(0)
	{}

	explicit CRefCountedAdapter(const T & src)
		: T(src)
		, m_dwRefCounter(0)
	{}
	
	template<class U1>
		explicit CRefCountedAdapter(U1 u1)
		: T(u1)
		, m_dwRefCounter(0)
	{}
	
	template<class U1, class U2>
		CRefCountedAdapter(U1 u1, U2 u2)
		: T(u1, u2)
		, m_dwRefCounter(0)
	{}
	
	template<class U1, class U2, class U3>
		CRefCountedAdapter(U1 u1, U2 u2, U3 u3)
		: T(u1, u2, u3)
		, m_dwRefCounter(0)
	{}

	template<class U1, class U2, class U3, class U4>
		CRefCountedAdapter(U1 u1, U2 u2, U3 u3, U4 u4)
		: T(u1, u2, u3, u4)
		, m_dwRefCounter(0)
	{}

	template<class U1, class U2, class U3, class U4, class U5>
		CRefCountedAdapter(U1 u1, U2 u2, U3 u3, U4 u4, U5 u5)
		: T(u1, u2, u3, u4, u5)
		, m_dwRefCounter(0)
	{}

	template<class U1, class U2, class U3, class U4, class U5, class U6>
		CRefCountedAdapter(U1 u1, U2 u2, U3 u3, U4 u4, U5 u5, U6 u6)
		: T(u1, u2, u3, u4, u5, u6)
		, m_dwRefCounter(0)
	{}

	template<class U1, class U2, class U3, class U4, class U5, class U6, class U7, class U8, class U9, class U10>
	CRefCountedAdapter(U1 u1, U2 u2, U3 u3, U4 u4, U5 u5, U6 u6, U7 u7, U8 u8, U9 u9, U10 u10)
		: T(u1, u2, u3, u4, u5, u6, u7, u8, u9, u10)
		, m_dwRefCounter(0)
	{}

	LONG & RefCounter() const
	{
		return m_dwRefCounter;
	}

private:
	mutable LONG m_dwRefCounter;
};

// -------------------------------------------------------------------------- //

class CRefCountedAdapterBase
{
public:
	CRefCountedAdapterBase()
		: m_dwRefCounter(0)
	{}
	virtual ~CRefCountedAdapterBase() {}


	LONG & RefCounter() const
	{
		return m_dwRefCounter;
	}

private:
	mutable LONG m_dwRefCounter;
};
